﻿using SoundEngine3;

namespace ZquenceStudio3Host.DspPlugins.Synthesizers.Nodesizer.Engine.Modules.HighLevel
{
    [ModuleDisplay("Sidechaning")]
    class SidechaningModule : BaseModule
    {
        public readonly ModuleInput Input;
        public readonly ModuleOutput Output;

        private InputModuleParameter Volyme;
        private InputModuleParameter Fallback;

        private int mChainigPosition;

        public SidechaningModule(ModuleManager manager) 
            : base(manager)
        {
            AddInput(Input = new ModuleInput("In",  IOType.Audio, this));
            AddOutput(Output = new ModuleOutput("Out", IOType.Audio, this));

            // Volyme.
            Volyme = new InputModuleParameter(this, "Vol", 0, IOType.Audio);
            Volyme.Label = "Volyme";
            Volyme.MinValue = 0;
            Volyme.MaxValue = 1;
            AddModuleParameter(Volyme);

            // Fallback.
            Fallback = new InputModuleParameter(this, "Fall", 500, IOType.Audio);
            Fallback.Label = "Volyme";
            Fallback.MinValue = 0;
            Fallback.MaxValue = 4000;
            AddModuleParameter(Fallback);
        }
        public override void Process()
        {
            if (Input.IsConnected)
            {
                double deltaOffsetInSamples = SoundEngineContext.PPQ_To_Samples(1);
                mChainigPosition = (int)((SoundEngineContext.PpqPosition * deltaOffsetInSamples) % (deltaOffsetInSamples * SoundEngineContext.PpqResolution));

                ProcessSidechaining(Input, Output, SoundEngineContext, ref mChainigPosition);
            }
        }

        private void ProcessSidechaining(ModuleInput input, ModuleOutput output, SoundEngineContext soundEngine, ref int chainingPosition)
        {
            double deltaOffsetInSamples = soundEngine.PPQ_To_Samples(1);
            double[] sound_left = input.FromOutput.AudioBuffer.Buffer;
            double[] sound_left_out = output.AudioBuffer.Buffer;

            int FALLBACK = (int)Fallback[0];

            int offset = (int)(deltaOffsetInSamples * soundEngine.PpqResolution);
            int offset_release = offset - FALLBACK;

            double offset_1 = 1.0 / offset_release;
            double offset_release_1 = 1.0 / FALLBACK;

            double vol = 1.0;
            int stop = soundEngine.BufferSize;
            for (int i = 0; i < stop; i++)
            {
                // Down.
                if (++chainingPosition >= offset_release)
                {
                    vol = offset_release_1 * (FALLBACK + (offset_release - chainingPosition));

                    if (chainingPosition == offset)
                    {
                        chainingPosition = 0;
                    }
                }
                else // Up.
                {
                    vol = offset_1 * chainingPosition;
                }
                
                double amplitude = Volyme[i];
                double add = vol * (1.0 - amplitude);
                vol = amplitude + add;

                sound_left_out[i] = sound_left[i] * vol;
            }
        }
    }
}
